package com.k8.rpc.invoke.invocation;

import com.k8.rpc.invoke.InvokeMode;
import com.k8.rpc.util.RpcUtils;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class RpcInvocation implements Invocation, Serializable {
    private Object[] arguments;
    private Map<String, Object> attachments;
    private String invocationKey;

    private transient Invocation templateInvocation;
    private final transient Lock attachmetsLock = new ReentrantLock(false);

    //调用方式是阻塞的还是非阻塞的（无返回值），还是Future
    private transient InvokeMode invokeMode;
    private transient Method method;

    public RpcInvocation() {
    }

    public RpcInvocation(Method method, Object[] arguments, Map<String, Object> attachments, String invocationKey, InvokeMode invokeMode) {
        this.arguments = arguments;
        if (attachments == null) this.attachments = new HashMap<>();
        this.method = method;
        this.invokeMode = invokeMode;
        this.invocationKey = invocationKey;
    }

    @Override
    public String getInvocationKey() {
        if (templateInvocation != null) {
            return templateInvocation.getInvocationKey();
        }
        return this.invocationKey;
    }


    @Override
    public Map<String, String> getAttachments() {
        Map<String, String> stringMap = new HashMap<>();
        this.attachments.keySet().stream().forEach(key -> {
            Object value = this.attachments.get(key);
            if (value instanceof String) {
                stringMap.put(key, (String) value);
            }
        });
        return stringMap;
    }

    @Override
    public Map<String, Object> getObjectAttachments() {
        return this.attachments;
    }

    @Override
    public Map<String, Object> copyObjectAttachments() {
        return Map.copyOf(attachments);
    }

    @Override
    public Object[] getArguments() {
        return this.arguments;
    }

    @Override
    public void setAttachment(String key, Object value) {
        attachmetsLock.lock();
        try {
            attachments.put(key, value);
        } finally {
            attachmetsLock.unlock();
        }
    }

    @Override
    public void setObjectAttachmentIfAbsent(String key, Object value) {
        Object result = attachments.get(key);
        if (result == null) {
            try {
                attachmetsLock.lock();
                if (attachments.get(key) == null) {
                    attachments.put(key, value);
                }
            } finally {
                attachmetsLock.unlock();
            }
        }
    }

    @Override
    public String getAttachment(String key) {
        Object value = attachments.get(key);
        if (value == null) return null;
        return (String) value;
    }

    @Override
    public Object getObjectAttachment(String key) {
        Object value = attachments.get(key);
        if (value == null) return null;
        return value;
    }

    @Override
    public String getAttachment(String key, String defaultValue) {
        String value = getAttachment(key);
        if (value == null) return defaultValue;
        return value;
    }

    @Override
    public Object getObjectAttachment(String key, Object defaultValue) {
        Object value = getObjectAttachment(key);
        if (value == null) return defaultValue;
        return value;
    }


    @Override
    public InvokeMode getInvokeMode() {
        if (templateInvocation != null) {
            return templateInvocation.getInvokeMode();
        }
        return this.invokeMode;
    }

    @Override
    public String getMethodName() {
        if (templateInvocation != null) {
            return templateInvocation.getMethodName();
        }
        return method.getName();
    }

    @Override
    public Type getReturnType() {
        if (templateInvocation != null) {
            return templateInvocation.getReturnType();
        }
        return method.getGenericReturnType();
    }

    @Override
    public void setTemplate(Invocation invocation) {
        this.templateInvocation = invocation;
    }


    @Override
    public Invocation getTemplate() {
        return templateInvocation;
    }

    @Override
    public Class<?>[] getParameterTypes() {
        if (templateInvocation != null) {
            return templateInvocation.getParameterTypes();
        }
        return this.method.getParameterTypes();
    }

    public void setArguments(Object[] arguments) {
        this.arguments = arguments;
    }

    public void setAttachments(Map<String, Object> attachments) {
        this.attachments = attachments;
    }

    public void setInvocationKey(String invocationKey) {
        this.invocationKey = invocationKey;
    }
}
